﻿@using System.Globalization

@namespace MudBlazor.Charts

@inherits MudChartBase<T, HeatMapChartOptions>

@typeparam T

@inject InternalMudLocalizer Localizer

@{
    var series = _series.Where(x => x.Visible).ToList();
}

<svg @ref="_elementReference" @attributes="UserAttributes" class="mud-chart-heat mud-ltr" width="@Width" height="@Height" viewBox="0 0 @_boundWidth.ToStr() @_boundHeight.ToStr()" style="@HoveredStylename">
    @if (_options is { EnableSmoothGradient: true })
    {
        <defs>
            @* 4-point gradients for each cell *@
            @for (int row = 0; row < series.Count; row++)
            {
                var dataLength = series[row].Data?.Values.Count ?? 0;
                for (int col = 0; col < dataLength; col++)
                {
                    var cell = _heatMapCells.FirstOrDefault(c => c.Row == row && c.Column == col);
                    var color = GetColorForValue(cell?.Value);

                    // Define each gradient for 4 directions

                    <linearGradient id="gradient-topcenter-@row-@col" x1="50%" y1="0%" x2="50%" y2="50%">
                        <stop offset="0%" style="stop-color:@GetColorForValue(GetDataValue(row - 1, col))" />
                        <stop offset="100%" style="stop-color:@color" />
                    </linearGradient>

                    <linearGradient id="gradient-right-@row-@col" x1="100%" y1="50%" x2="50%" y2="50%">
                        <stop offset="0%" style="stop-color:@GetColorForValue(GetDataValue(row, col + 1))" />
                        <stop offset="100%" style="stop-color:@color" />
                    </linearGradient>

                    <linearGradient id="gradient-bottomcenter-@row-@col" x1="50%" y1="100%" x2="50%" y2="50%">
                        <stop offset="0%" style="stop-color:@GetColorForValue(GetDataValue(row + 1, col))" />
                        <stop offset="100%" style="stop-color:@color" />
                    </linearGradient>

                    <linearGradient id="gradient-left-@row-@col" x1="0%" y1="50%" x2="50%" y2="50%">
                        <stop offset="0%" style="stop-color:@GetColorForValue(GetDataValue(row, col - 1))" />
                        <stop offset="100%" style="stop-color:@color" />
                    </linearGradient>
                }
            }
        </defs>
    }

    @for (int row = 0; row < series.Count; row++)
    {
        var rowIndex = row;
        var rowLabel = series[row].Name;

        // how tall each cell should be
        var y = (row * (_cellDimension.Height + _cellDimension.Padding)) + _verticalStartSpace;
        var seriesLength = series[row].Data != null ? series[row].Data.Values.Count : 0;

        @for (int col = 0; col < seriesLength; col++)
        {
            var colIndex = col;
            var cell = _heatMapCells.FirstOrDefault(c => c.Row == row && c.Column == col);
            var x = (col * (_cellDimension.Width + _cellDimension.Padding)) + _horizontalStartSpace;

            // Gradient Overlays
            @if (_options is { EnableSmoothGradient: true })
            {
                // Apply the 4 gradient overlays with partial opacity
                <rect x="@x.ToStr()"
                      y="@y.ToStr()"
                      width="@_cellDimension.Width.ToStr()"
                      height="@_cellDimension.Height.ToStr()"
                      fill="url(#gradient-topcenter-@row-@col)"
                      opacity="0.25" />

                <rect x="@x.ToStr()"
                      y="@y.ToStr()"
                      width="@_cellDimension.Width.ToStr()"
                      height="@_cellDimension.Height.ToStr()"
                      fill="url(#gradient-right-@row-@col)"
                      opacity="0.25" />

                <rect x="@x.ToStr()"
                      y="@y.ToStr()"
                      width="@_cellDimension.Width.ToStr()"
                      height="@_cellDimension.Height.ToStr()"
                      fill="url(#gradient-bottomcenter-@row-@col)"
                      opacity="0.25" />

                <rect x="@x.ToStr()"
                      y="@y.ToStr()"
                      width="@_cellDimension.Width.ToStr()"
                      height="@_cellDimension.Height.ToStr()"
                      fill="url(#gradient-left-@row-@col)"
                      opacity="0.25" />
            }


            <g class="mud-chart-cell">
                <!-- Group containing both the rectangle and the text -->
                <g>
                    @{

                        var cellColor = _options is { EnableSmoothGradient: true }
                                ? "transparent"
                                : cell?.MudColor?.ToString(Utilities.MudColorOutputFormats.RGBA) ?? GetColorForValue(cell?.Value);
                    }
                    <rect x="@x.ToStr()"
                          y="@y.ToStr()"
                          width="@_cellDimension.Width.ToStr()"
                          height="@_cellDimension.Height.ToStr()"
                          @onclick="async () => await SetSelectedCellAsync(rowIndex, colIndex)"
                          @onmouseover="(e) => OnCellMouseOver(e, cell)"
                          @onmouseout="OnCellMouseOut"
                          fill="@(cellColor)" />

                    @if (_options is { ShowLabels: true } && cell is { Value: not null } && cell is { CustomFragment: null })
                    {
                        <g>
                            <text @onclick="async () => await SetSelectedCellAsync(rowIndex, colIndex)"
                                  @onmouseover="(e) => OnCellMouseOver(e, cell)"
                                  @onmouseout="OnCellMouseOut"
                                  font-size="@_dynamicFontSize.ToStr()"
                                  x="@((x + _cellDimension.Width / 2).ToStr())"
                                  y="@((y + _cellDimension.Height / 2).ToStr())"
                                  dominant-baseline="middle"
                                  text-anchor="middle">
                                  @FormatValueForDisplay(cell?.Value)
                            </text>
                        </g>
                    }
                    else if (cell is { CustomFragment: not null })
                    {
                        @if (cell.Width.HasValue || cell.Height.HasValue)
                        {
                            // If specific width/height are provided, scale to cell dimensions
                            var tempWidth = cell.Width ?? _cellDimension.Width;
                            var tempHeight = cell.Height ?? _cellDimension.Height;

                            <svg x="@x.ToStr()"
                                 y="@y.ToStr()"
                                 @onclick="async () => await SetSelectedCellAsync(rowIndex, colIndex)"
                                 width="@_cellDimension.Width.ToStr()"
                                 height="@_cellDimension.Height.ToStr()"
                                 viewBox="0 0 @tempWidth.ToStr() @tempHeight.ToStr()"
                                 preserveAspectRatio="xMidYMid meet">
                                 @cell.CustomFragment
                            </svg>
                        }
                        else
                        {
                            // If no specific width/height, use cell dimensions with padding
                            <foreignObject x="@x.ToStr()"
                                           y="@y.ToStr()"
                                           @onclick="async () => await SetSelectedCellAsync(rowIndex, colIndex)"
                                           width="@_cellDimension.Width.ToStr()"
                                           height="@_cellDimension.Height.ToStr()">
                                <div xmlns="http://www.w3.org/1999/xhtml" style="width:100%; height:100%;display:flex; align-items:center; justify-content:center;">
                                    @cell.CustomFragment
                                </div>
                            </foreignObject>
                        }
                    }
                </g>
            </g>

            // XAxis Labels
            var xAxisLabels = ChartLabels ?? [];
            @if (row == 0 && _options?.XAxisLabelPosition == XAxisLabelPosition.Top && col < xAxisLabels.Length)
            {
                <g transform="translate(@((x + _cellDimension.Width / 2).ToStr()), @((y - CellPadding).ToStr()))">
                    <text class="mud-charts-xaxis" x="0" y="0" text-anchor="middle" font-size="@_dynamicFontSize.ToStr()">@xAxisLabels[col]</text>
                </g>
            }
            else if (row == RowCount - 1 && _options?.XAxisLabelPosition == XAxisLabelPosition.Bottom && col < xAxisLabels.Length)
            {
                <g transform="translate(@((x + _cellDimension.Width / 2).ToStr()), @((y + _cellDimension.Height + _dynamicFontSize).ToStr()))">
                    <text class="mud-charts-xaxis" x="0" y="0" text-anchor="middle" fill="black" font-size="@_dynamicFontSize.ToStr()">@xAxisLabels[col]</text>
                </g>
            }
        }

        // YAxis Labels
        @if (_options?.YAxisLabelPosition == YAxisLabelPosition.Left)
        {
            <g transform="translate(@((_horizontalStartSpace - CellPadding).ToStr()), @((y + _cellDimension.Height / 2).ToStr()))">
                @((MarkupString)$"<text class='mud-charts-yaxis' x='0' y='0' dominant-baseline='middle' text-anchor='end' font-size='{_dynamicFontSize.ToStr()}'>{rowLabel}</text>")
            </g>
        }
        else if (_options?.YAxisLabelPosition == YAxisLabelPosition.Right)
        {
            <g transform="translate(@((_horizontalStartSpace + HeatmapWidth + CellPadding).ToStr()), @((y + _cellDimension.Height / 2).ToStr()))">
                @((MarkupString)$"<text class='mud-charts-yaxis' x='0' y='0' dominant-baseline='middle' text-anchor='start' font-size='{_dynamicFontSize.ToStr()}'>{rowLabel}</text>")
            </g>
        }
    }
    @if (_options is { ShowLegend: true })
    {
        var (legendX, legendY) = GetLegendPosition();
        var labelFontSize = LegendFontSize - 2;
        @switch (_legendPosition)
        {
            case Position.Top or Position.Bottom:
                {
                    var legendPadding = _options is { ShowLegendLabels: true } ? CellPadding * 2 : CellPadding;
                    var totalChars = 8;
                    // width is horizontal, cellpadding between the word Less and the middle section and the last word
                    var textWidth = (totalChars * LegendFontSize * AverageCharWidthMultiplier) + (CellPadding * 2);
                    var totalWidth = (_legends.Count * (LegendBox + legendPadding)) - legendPadding + textWidth;
                    var offsetX = -totalWidth / 2; // Center horizontally
                    var offsetY = LegendBox / 2; // Center vertically

                    <g transform="translate(@((legendX + offsetX).ToStr()), @(legendY.ToStr()))">
                        <!-- "Less" label -->
                        <text class="mud-charts-xaxis" 
                        x="0"
                        y="0"
                        dominant-baseline="middle"
                        text-anchor="start"
                        font-size="@LegendFontSize">@Localizer[Resources.LanguageResource.HeatMap_Less]
                        </text>
                        @for (int i = 0; i < _legends.Count; i++)
                        {
                            var index = i;
                            var x = (textWidth / 2) + (i * (LegendBox + legendPadding));
                            var tooltipX = legendX + offsetX + x + (LegendBox / 2);

                            <g transform="translate(@x.ToStr(), 0)" class="mud-chart-heatmap-legend">
                                <rect @onmouseover="(e) => OnLegendMouseOver(e, _legends[index], new((float)tooltipX , (float)legendY))"
                                @onmouseout="OnLegendMouseOut"
                                x="0" 
                                y="@((-LegendBox / 2).ToStr())" 
                                width="@LegendBox.ToStr()" 
                                height="@LegendBox.ToStr()" 
                                fill="@_legends[i].color">
                                </rect>
                                @if (_options is { ShowLegendLabels: true })
                                {
                                    var x1 = LegendBox / 2;
                                    var x2 = x1;
                                    var y1 = _legendPosition == Position.Top ? -offsetY : offsetY;
                                    var y2 = _legendPosition == Position.Top ? -offsetY - LegendLineLength : offsetY + LegendLineLength;

                                    <line x1="@x1.ToStr()" 
                                    y1="@y1.ToStr()" 
                                    x2="@x2.ToStr()" 
                                    y2="@y2.ToStr()" 
                                    stroke-width="1" />
                                    var y3 = y2 + (_legendPosition == Position.Top ? -LegendLineLength : LegendLineLength);
                                    @((MarkupString)@$"<text x='0' y='{y3.ToStr()}' dominant-baseline='middle' text-anchor='start' font-size='{labelFontSize}'>{FormatValueForDisplay(_legends[i].value)}</text>")
                                }
                            </g>
                        }

                        <!-- "More" label -->
                        <text class="mud-charts-xaxis"
                        x="@(((textWidth / 2) + (_legends.Count * (LegendBox + legendPadding))).ToStr())"
                        y="0"
                        dominant-baseline="middle"
                        text-anchor="start"
                        font-size="@LegendFontSize">@Localizer[Resources.LanguageResource.HeatMap_More]
                        </text>
                    </g>

                }
                break;

            case Position.Right or Position.Left:
                {
                    var textHeight = (8 * LegendFontSize) + (CellPadding * 2);
                    var totalHeight = (_legends.Count * (LegendBox + CellPadding)) - CellPadding + textHeight;

                    var offsetX = -1.0; // Center horizontally
                    var offsetY = -totalHeight / 2; // Center vertically

                    <g transform="translate(@legendX.ToStr(), @((legendY + offsetY).ToStr()))">
                        <!-- "Less" label -->
                        <text class="mud-charts-xaxis" 
                        x="@offsetX.ToStr()" 
                        y="@(((textHeight / 2) - CellPadding - LegendBox).ToStr())" 
                        dominant-baseline="middle" 
                        text-anchor="start" 
                        font-size="@LegendFontSize">@Localizer[Resources.LanguageResource.HeatMap_Less]
                        </text>

                        @for (int i = 0; i < _legends.Count; i++)
                        {
                            var index = i;
                            var y = (textHeight / 2) + (i * (LegendBox + CellPadding));
                            var tooltipY = legendY + offsetY + y;

                            <g transform="translate(0, @y.ToStr())" class="mud-chart-heatmap-legend">
                                <rect @onmouseover="(e) => OnLegendMouseOver(e, _legends[index], new((float)(legendX + (LegendBox / 2)) , (float)tooltipY))"
                                @onmouseout="OnLegendMouseOut"
                                x="0" 
                                y="@((-LegendBox / 2).ToStr())" 
                                width="@LegendBox.ToStr()" 
                                height="@LegendBox.ToStr()" 
                                fill="@_legends[i].color">
                                </rect>
                                @if (_options is { ShowLegendLabels: true })
                                {
                                    double y1 = 0, y2 = 0, x1 = _legendPosition == Position.Right ? LegendBox : 0;
                                    double x2 = _legendPosition == Position.Right ? LegendBox + LegendLineLength : -LegendLineLength;
                                    var x3 = x2 + (_legendPosition == Position.Right ? CellPadding : -CellPadding);

                                    <line x1="@x1.ToStr()" 
                                    y1="@y1.ToStr()" 
                                    x2="@x2.ToStr()" 
                                    y2="@y2.ToStr()" 
                                    stroke-width="1" />

                                    @((MarkupString)@$"<text x='{x3.ToStr()}' y='0' dominant-baseline='middle' text-anchor='{(_legendPosition == Position.Right ? "start" : "end")}' font-size='{labelFontSize}'>{FormatValueForDisplay(_legends[i].value)}</text>")
                                }
                            </g>
                        }

                        <!-- "More" label -->
                        <text class="mud-charts-xaxis"
                        x="@offsetX.ToStr()"
                        y="@(((textHeight / 2) + ((_legends.Count) * (LegendBox + CellPadding))).ToStr())" 
                        dominant-baseline="middle" 
                        text-anchor="start" 
                        font-size="@LegendFontSize">@Localizer[Resources.LanguageResource.HeatMap_More]
                        </text>
                    </g>
                }
                break;
        }
    }

    <!-- Tooltip applied to the group -->
    @if (_options is { ShowToolTips: true } && _hoveredCell is not null)
    {
        var color = GetColorForValue(_hoveredCell.Value);
        var colOffset = _hoveredCell.Column * (_cellDimension.Width + _cellDimension.Padding);
        var rowOffset = _hoveredCell.Row * (_cellDimension.Height + _cellDimension.Padding);
        var x = colOffset + _horizontalStartSpace + (_cellDimension.Width / 2);
        var y = rowOffset + _verticalStartSpace + (_cellDimension.Height / 2);

        var path = new SvgPath()
        {
            Index = _hoveredCell.Row,
            LabelX = x,
            LabelY = y,
            LabelXValue = ChartLabels.Length > _hoveredCell.Column ? ChartLabels[_hoveredCell.Column] : string.Empty,
            LabelYValue = _hoveredCell.Value.ToString(),
        };

        var (tooltipX, tooltipY) = TooltipPositionFunc?.Invoke(path) is { } pos
                ? (pos.X, pos.Y)
                : (path.LabelX, path.LabelY);

        if (TooltipTemplate is not null)
        {
            <g class="mud-chart-tooltip" transform="translate(@tooltipX.ToStr(), @tooltipY.ToStr())">
                @TooltipTemplate((path, color))
            </g>
        }
        else
        {
            var hoveredSeries = ChartSeries[_hoveredCell.Row];
            var tooltipTitleFormat = hoveredSeries.TooltipTitleFormat ?? ChartOptions.TooltipTitleFormat;
            var tooltipSubtitleFormat = hoveredSeries.TooltipSubtitleFormat ?? ChartOptions.TooltipSubtitleFormat;

            if (!string.IsNullOrWhiteSpace(tooltipTitleFormat))
            {
                var tooltipTitle = tooltipTitleFormat
                .Replace("{{SERIES_NAME}}", hoveredSeries.Name)
                .Replace("{{X_VALUE}}", ChartLabels.Length > _hoveredCell.Column ? ChartLabels[_hoveredCell.Column] : string.Empty)
                .Replace("{{Y_VALUE}}", _hoveredCell.Value.ToString());

                var tooltipSubtitle = tooltipSubtitleFormat?
                .Replace("{{SERIES_NAME}}", hoveredSeries.Name)
                .Replace("{{X_VALUE}}", ChartLabels.Length > _hoveredCell.Column ? ChartLabels[_hoveredCell.Column] : string.Empty)
                .Replace("{{Y_VALUE}}", _hoveredCell.Value.ToString()) ?? string.Empty;

                <ChartTooltip Title="@tooltipTitle" Subtitle="@tooltipSubtitle" Color="@color" X="@tooltipX" Y="@tooltipY" />
            }
        }
    }
    @if (_options is { ShowToolTips: true }  && _hoveredLegend is not null)
    {
        <ChartTooltip Title="@_hoveredLegend.Value.value.ToString()" Color="@_hoveredLegend.Value.color" X="@_hoveredLegendPosition.X" Y="@_hoveredLegendPosition.Y" />
    }
</svg>
@if (CanHideSeries is true)
{
    <Legend T="T" Data="@_toggleLegend" />
}
